package net.sf.rose.file.util.resource;

import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import net.sf.rose.util.RoseRuntimeException;
import net.sf.rose.util.StringUtil;

/** 
 * 类说明 ：代替JDK自带的配置文件加载器
 * 特别地：
 * 1、支持配置文件参数的修改和保存
 * 2、参数列表是有顺序结构的
 * @author fengjian E-mail: 9110530@qq.com 
 * @version 创建时间：2015年9月25日 下午4:37:57 
 */
public class ResourceProFile {

	private static Logger LOG = Logger.getLogger(ResourceProFile.class);

	/** 配置文件参数是否被更新标志 */
	private boolean updateFlag = false;

	/** 配置文件对象 */
	private File file = null;

	/**
	 * 配置文件全文内容 
	 * 其中参数只存参数名称，注释存储全文
	 */
	private List<String> content = new ArrayList<>();

	/** 所有参数的键值对 */
	private Map<String, String> paramemters = new LinkedHashMap<>();

	/** 父配置文件类 */
	private ResourceProFile parent = null;

	/**
	 * 设置文件对象
	 * @param file 文件对象
	 */
	public void setFile(File file) {
		this.file = file;
	}

	/**
	 * 获取文件对象
	 * @return 文件对象
	 */
	public File getFile() {
		return file;
	}

	/**
	 * 将所有数据读入内存
	 * @param file 文件对象
	 */
	public void load(File file) {
		this.file = file;
		try {
			InputStream in = new FileInputStream(file);
			this.load(in);
			in.close();
			LOG.warn("成功读取配置文件：" + file.getAbsolutePath());
		} catch (FileNotFoundException e) {
			String mess = "在指定的路径下没有找到配置文件：" + file.getAbsolutePath();
			LOG.error(mess, e);
			throw new RoseRuntimeException(mess, e);
		} catch (IOException e) {
			String mess = "读取配置文件：" + file.getAbsolutePath() + "时发生错误！";
			LOG.error(mess, e);
			throw new RoseRuntimeException(mess, e);
		}
	}

	/**
	 * 将所有数据读入内存
	 * @param input 输入文件流
	 * @throws IOException 文件操作异常
	 */
	public void load(InputStream input) throws IOException {
		int len = 1024;
		char[] buf = new char[len];
		BufferedInputStream in = new BufferedInputStream(input);
		int i = -1;
		int off = 0;
		int type = 0;// 0:注释;1:内容
		char b = 0;
		while ((i = in.read()) != -1) {
			b = (char) i;
			if (b == '\n') {
				readLine(type, buf, off);
				type = 1;
				off = 0;
			} else if (b == '#') {
				type = 0;
				buf[off] = b;
				off++;
			} else {
				// 如果缓存已满就扩大一倍
				if (off == len) {
					len = len * 2;
					char[] buf2 = new char[len];
					for (int j = 0; j < buf.length; j++) {
						buf2[j] = buf[j];
					}
					buf = buf2;
				}
				buf[off] = b;
				off++;
			}
		}
		// 读取最后一行数据
		buf[off] = b;
		off++;
		readLine(type, buf, off);
	}

	/**
	 * 读取一行数据
	 * @param type 内容类型
	 * @param buf 字节码
	 * @param off 偏移量
	 */
	private void readLine(int type, char[] buf, int off) {
		// 1:内容
		if (type == 1) {
			boolean hasEqual = false;
			int index = 0;
			for (; index < off; index++) {
				if (buf[index] == '=') {
					hasEqual = true;
					break;
				}
			}
			if (hasEqual) {
				String key = StringUtil.fromUnicode(buf, 0, index);
				String val = StringUtil.fromUnicode(buf, index + 1, off - (index + 2));
				content.add(key);
				paramemters.put(key, val);
				// System.out.println(key + '=' + val);
			}
		}
		// 0:注释
		else {
			// content.add(new String(buf, 0, off));
			// System.out.println(new String(buf, 0, off));
			content.add(StringUtil.fromUnicode(buf, 0, off));
		}
	}

	/**
	 * 保存
	 * @return 是否成功
	 */
	public boolean save() {
		if (!updateFlag) {
			return false;
		}
		try {
			BufferedWriter buf = new BufferedWriter(new FileWriter(file));
			for (String s : content) {
				if (s.charAt(0) == '#') {
					String s1 = StringUtil.toUnicode(s, false);
					buf.write(s1.substring(1, s1.length() - 2));
				} else {
					String key = StringUtil.toUnicode(s, false);
					String val = StringUtil.toUnicode(paramemters.get(s), false);
					buf.write(key + '=' + val);
				}
				buf.newLine();
			}
			buf.flush();
			buf.close();
			return true;
		} catch (IOException e) {
			String mess = "保存配置文件：" + file.getAbsolutePath() + "时发生错误！";
			LOG.error(mess, e);
			return false;
		}
	}

	/**
	 * 设置父配置文件类
	 * @param parent 父配置文件实体
	 */
	public void setParent(ResourceProFile parent) {
		this.parent = parent;
	}

	/**
	 * 添加注释
	 * @param comment 注释内容
	 */
	public void addComment(String comment) {
		content.add('#' + comment);
	}

	/**
	 * 设置属性
	 * @param key 键名
	 * @param value 键值
	 */
	public void set(String key, String value) {
		if (!content.contains(key)) {
			content.add(key);
		}
		paramemters.put(key, value);
		updateFlag = true;
	}

	/**
	 * 设置属性
	 * @param ref 注释
	 * @param key 键名
	 * @param value 键值
	 */
	public void insert(String ref, String key, String value) {
		if (!content.contains(key)) {
			content.add(content.indexOf(ref), key);
		}
		paramemters.put(key, value);
		updateFlag = true;
	}

	/**
	 * 取得属性
	 * @param key 主键
	 * @return 属性值
	 */
	public String get(String key) {
		String str = paramemters.get(key);
		// 如果自身配置文件没有找到属性，而且父配置文件存在，则从父配置文件中找
		if (str == null) {
			return parent != null ? parent.get(key) : null;
		} else {
			return str.trim();
		}
	}

	/**
	 * 删除属性
	 * @param key 主键
	 */
	public void remove(String key) {
		paramemters.remove(key);
		content.remove(key);
		updateFlag = true;
	}

	/**
	 * 返回所有键值
	 * @return 所有键值
	 */
	public Map<String, String> getAllParamemters() {
		Map<String, String> map = new LinkedHashMap<>();
		if (parent != null) {
			map.putAll(parent.getAllParamemters());
		}
		map.putAll(paramemters);
		return map;
	}

	/**
	 * 获取所有参数
	 * @return 所有参数
	 */
	public Map<String, String> getParamemters() {
		return paramemters;
	}

	/**
	 * 设置所有参数
	 * @param paramemters 所有参数
	 */
	public void setParamemters(Map<String, String> paramemters) {
		this.paramemters = paramemters;
	}

}
